/**
 * Fichier Identification.java : Définition de classe
 * fr.unice.gnpt.gui.Identification : Boite de dialogue d'identification
 * 
 * Auteurs : Groupe GNPT : Gastinel, Nicolas, Pellegrino, Trovato
 */
package fr.unice.gnpt.gui;

import java.awt.event.KeyEvent;
import java.io.File;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JProgressBar;
import javax.swing.JTextField;

import net.miginfocom.swing.MigLayout;
import fr.unice.gnpt.Main;
import fr.unice.gnpt.core.dao.factory.TypeFabriqueDao;
import fr.unice.gnpt.core.util.Parametres;
import fr.unice.gnpt.core.util.ParametresException;
import fr.unice.gnpt.gui.listeners.IdentificationListener;

/**
 * Classe graphique d'identification à la base (Enseignant/Coordonateur).
 * <p>
 * Cette classe est la première à être affichée lors du lancement de
 * l'application. Elle permet de choisir le type d'utilisateur (i.e. Enseignant
 * ou Coordinateur), ainsi que le choix du gestionnaire de persistance et des
 * propriétés de connexion associés. Le choix du nom d'utilisateur (dans le cas
 * Enseignant) sera effectué par une autre classe.
 * 
 * @author gnpt
 * @version 0.2.3
 */
public class Identification extends JDialog {
	private static final long serialVersionUID = 1L;
	/**
	 * Contient le logo de l'application.
	 */
	private JLabel icon;
	/**
	 * Liste de choix du type d'utilisateur.
	 */
	private JComboBox typeUtilisateur = new JComboBox(new String[] {
			"Enseignant", "Coordonnateur" });
	/**
	 * Liste du choix du type de persistance.
	 */
	private JComboBox typePersistance = new JComboBox(new String[] { "DB4O",
			"JPA" });
	/**
	 * Liste du choix des types de base de données disponibles pour JPA.
	 */
	private JComboBox typeBdd = new JComboBox(
			new String[] { "Oracle", "MySQL" });
	/**
	 * Chemin de fichier DB4O.
	 */
	private JTextField db4oChemin = new JTextField(10);
	/**
	 * Label associé au JTextField indiquant le chemin du fichier DB4O.
	 */
	private JLabel db4oCheminLabel = new JLabel("Base DB4O :");
	/**
	 * URL de la base de données.
	 */
	private JTextField serveur = new JTextField(15);
	/**
	 * Port de la base de données.
	 */
	private JTextField port = new JTextField(5);
	/**
	 * Nom de la base de donnée à utiliser.
	 */
	private JTextField database = new JTextField(15);
	/**
	 * Utilisateur de la base de données.
	 */
	private JTextField user = new JTextField(15);
	/**
	 * Mot de passe associé à l'utilisateur de la base de données.
	 */
	private JTextField pass = new JPasswordField(15);
	/**
	 * Bouton permettant de continuer.
	 */
	private JButton next = new JButton("Suivant");
	/**
	 * Bouton permettant de quitter.
	 */
	private JButton exit = new JButton("Quitter");
	/**
	 * Label associé au serveur de la base de données.
	 */
	private JLabel serveurLabel = new JLabel("Serveur :");
	/**
	 * Label associé au port de la base de données.
	 */
	private JLabel portLabel = new JLabel("Port :");
	/**
	 * Label associé au nom de la base de données à utiliser.
	 */
	private JLabel databaseLabel = new JLabel("Base de données :");
	/**
	 * Label associé au nom d'utilisateur de la base de données.
	 */
	private JLabel userLabel = new JLabel("Nom d'utilisateur :");
	/**
	 * Label associé au mot de passe de la base de données.
	 */
	private JLabel passLabel = new JLabel("Mot de passe :");
	/**
	 * Panels dans lequel on ajoute nos composants.
	 */
	private JPanel panel;
	/**
	 * Lien vers la gestion des événements.
	 */
	private IdentificationListener listener = new IdentificationListener(this);
	/**
	 * Type de connexion associé au type de base de données.
	 */
	private TypeFabriqueDao typeDao;
	/**
	 * Objet à réveiller lorsque la configuration est terminée et que
	 * l'utilisateur décide de continuer.
	 */
	private Object threadAttente;
	/**
	 * Les paramètres de l'application.
	 */
	private Parametres parametres;
	/**
	 * File Chooser permettant une meilleure ergonomie quand au choix du fichier
	 * pour DB4O.
	 */
	private JFileChooser choisirFichier = new JFileChooser(Parametres
			.getParametres().getCheminDb4o());
	/**
	 * Bouton servant à appeler le JFileChooser choisirFichier.
	 */
	private JButton boutonFileChooser = new JButton("Explorer");
	/**
	 * Barre de progression faisant patienter l'utilisateur durant le test de la
	 * connexion à la base de données lors de l'utilisation du programme avec
	 * JPA.
	 */
	private JProgressBar progressBar;

	/**
	 * Lance la fenêtre d'identification au logiciel.
	 */
	public Identification() {
		super();
		this.parametres = Parametres.getParametres();
	}

	/**
	 * Initialise et affiche la fenêtre d'identification et de paramètrage de la
	 * base de données.
	 */
	public void creerEtAfficher() {
		this.panel = new JPanel(new MigLayout("insets 20, fillx",
				"[]15[grow,fill]", "[]10[]"));

		// Message d'accueil avec icône
		this.icon = new JLabel();
		this.icon.setIcon(new ImageIcon(this.getClass().getResource(
				"images/logo.png"), "EducationManager"));
		this.panel.add(this.icon, "align center, gapY 0! 5!");

		JLabel welcome = new JLabel(
				"<html>Bienvenue dans le logiciel<br>de gestion de "
						+ "l'éducation.</html>");
		this.panel.add(welcome, "gapY 0! 5!, wrap");

		// Choix du profil : enseignant/coordonateur
		this.panel.add(new JLabel("Profil :"));
		this.panel.add(this.typeUtilisateur, "wrap");

		String utilisateur = this.parametres.getTypeUtilisateur();
		if (!utilisateur.equalsIgnoreCase("enseignant")) {
			this.typeUtilisateur.setSelectedIndex(1);
		}

		// Choix du type de base
		String gestionnaire = this.parametres.getGestionnairePersistance();
		this.typeDao = TypeFabriqueDao.valueOf(gestionnaire.toUpperCase());

		this.panel.add(new JLabel("Persistance :"));
		this.panel.add(this.typePersistance, "split 2");
		this.panel.add(this.typeBdd, "wrap");

		if (!gestionnaire.equalsIgnoreCase("db4o")) {
			this.typePersistance.setSelectedIndex(1);
		}

		// Initialisation du bon sous-panel
		this.setTypeConnexion();
		this.setPreferences();

		// Boutons de fin
		this.panel.add(this.next, "gapY 10! 5!, span 2, split 2, align center");

		this.next.setMnemonic(KeyEvent.VK_S);
		this.next.setActionCommand("valide");
		this.next.addActionListener(this.listener);
		this.exit.setMnemonic(KeyEvent.VK_Q);
		this.exit.setActionCommand("exit");
		this.exit.addActionListener(this.listener);
		this.panel.add(this.exit, "wrap");

		// Ajout des évenements
		this.typePersistance.setActionCommand("changementTypePersistance");
		this.typePersistance.addActionListener(this.listener);

		this.typeBdd.setActionCommand("changementTypeSGBD");
		this.typeBdd.addActionListener(this.listener);

		this.boutonFileChooser.setMnemonic(KeyEvent.VK_E);
		this.boutonFileChooser.setActionCommand("explorer");
		this.boutonFileChooser.addActionListener(this.listener);

		// Finalisation
		this.add(this.panel);

		this.setPreferences();
		this.setTitle("Paramètres de connexion");
		this.pack();
		this.setResizable(false);
		this.setLocationRelativeTo(null);
		this.setVisible(true);
	}

	/**
	 * Type d'utilisateur de l'application.
	 * 
	 * @return le type sélectionné.
	 */
	public int getTypeUtilisateur() {
		return this.typeUtilisateur.getSelectedIndex();
	}

	/**
	 * Introduit les préférences enregistrées dans le champs du formulaire.
	 */
	public void setPreferences() {
		this.setPreferences(true);
	}

	/**
	 * Introduit les préférences enregistrées dans le champs du formulaire.
	 * 
	 * @param setSGBD
	 *            un booléen pour indiquer si on force la mise à jour de la
	 *            JComboBox typeBdd.
	 */
	public void setPreferences(boolean setSGBD) {
		// Remplissage des JTextField avec les paramètres par défaut
		switch (this.getTypeDao()) {
		case JPA:
			String sgbd = this.parametres.getSGBD();
			if (setSGBD) {
				if (sgbd.equalsIgnoreCase("oracle")) {
					this.typeBdd.setSelectedIndex(0);
				} else if (sgbd.equalsIgnoreCase("mysql")) {
					this.typeBdd.setSelectedIndex(1);
				}
			}
			// Les paramètres
			String phote = "";
			String pport = "";
			String pbase = "";
			String puser = "";
			String ppass = "";
			if (this.typeBdd.getSelectedIndex() == 0) {
				phote = this.parametres.getHoteOracle();
				pport = this.parametres.getPortOracle();
				pbase = this.parametres.getBaseOracle();
				puser = this.parametres.getUtilisateurOracle();
				ppass = this.parametres.getMotDePasseOracle();
			} else {
				phote = this.parametres.getHoteMySql();
				pport = this.parametres.getPortMySql();
				pbase = this.parametres.getBaseMySql();
				puser = this.parametres.getUtilisateurMySql();
				ppass = this.parametres.getMotDePasseMySql();
			}
			this.serveur.setText(phote);
			this.port.setText(pport);
			this.database.setText(pbase);
			this.user.setText(puser);
			this.pass.setText(ppass);
			this.setFormulaireJPA();
			break;
		case DB4O:
			this.db4oChemin.setText(this.parametres.getCheminDb4o());
			this.setFormulaireDB4O();
			break;
		default:
			break;
		}
	}

	/**
	 * Enregistre les paramètes de connexion.
	 */
	public void sauverProprietes() {

		this.parametres.setTypeUtilisateur(this.typeUtilisateur
				.getSelectedItem().toString());

		switch (this.getTypeDao()) {
		case JPA:
			this.parametres.setGestionnairePersistance("jpa");
			String sgbd = this.typeBdd.getSelectedItem().toString();
			this.parametres.setSGBD(sgbd);
			if (sgbd.equalsIgnoreCase("oracle")) {
				this.parametres.setHoteOracle(this.serveur.getText());
				this.parametres.setPortOracle(this.port.getText());
				this.parametres.setBaseOracle(this.database.getText());
				this.parametres.setUtilisateurOracle(this.user.getText());
				this.parametres.setMotDePasseOracle(this.pass.getText());
			} else if (sgbd.equalsIgnoreCase("mysql")) {
				this.parametres.setHoteMySql(this.serveur.getText());
				this.parametres.setPortMySql(this.port.getText());
				this.parametres.setBaseMySql(this.database.getText());
				this.parametres.setUtilisateurMySql(this.user.getText());
				this.parametres.setMotDePasseMySql(this.pass.getText());
			}
			break;
		case DB4O:
			this.parametres.setGestionnairePersistance("db4o");
			this.parametres.setCheminDb4o(this.db4oChemin.getText());
			break;
		default:
			break;
		}
		// On force la synchronisation avec le système
		try {
			this.parametres.enregistrer();
		} catch (ParametresException e) {
			String msg = "Les paramètres n'ont pu être sauvegardés.";
			Main.getJournal().logException(e, msg, true, true);
			JOptionPane.showMessageDialog(null, msg, "Erreur",
					JOptionPane.ERROR_MESSAGE);
		}
	}

	/**
	 * Modifie le type de la base de données pour choisir le DAO qui convient.
	 */
	public void setTypeConnexion() {
		if (((String) this.typePersistance.getSelectedItem())
				.equalsIgnoreCase("JPA")) {
			this.setFormulaireJPA();
		} else {
			this.setFormulaireDB4O();
		}
	}

	/**
	 * Met en place le formulaire pour remplir les paramètres de connexion
	 * nécessaires pour JPA.
	 */
	public void setFormulaireJPA() {
		this.typePersistance.setSelectedIndex(1);
		this.setTypeDao(TypeFabriqueDao.JPA);
		this.supprimerFormulaireDB4O();
		this.ajouterFormulaireJPA();
		this.pack();
		this.setLocationRelativeTo(null);
	}

	/**
	 * Met en place le formulaire pour remplir les paramètres de connexion
	 * nécessaires pour DB4O.
	 */
	public void setFormulaireDB4O() {
		this.setTypeDao(TypeFabriqueDao.DB4O);
		this.typePersistance.setSelectedIndex(0);
		this.supprimerFormulaireJPA();
		this.ajouterFormulaireDB4O();
		this.pack();
		this.setLocationRelativeTo(null);
	}

	/**
	 * Ajoute au panel les composants nécessaires pour le formulaire DB4O.
	 */
	private void ajouterFormulaireDB4O() {
		this.panel.add(this.typePersistance, "wrap", 5);
		this.panel.add(this.db4oCheminLabel, 6);
		this.panel.add(this.db4oChemin, "split 2", 7);
		this.panel.add(this.boutonFileChooser, "wrap", 8);
	}

	/**
	 * Supprime du panel les composants du formulaire DB4O.
	 */
	private void supprimerFormulaireDB4O() {
		this.panel.remove(this.typePersistance);
		this.panel.remove(this.db4oCheminLabel);
		this.panel.remove(this.db4oChemin);
		this.panel.remove(this.boutonFileChooser);
	}

	/**
	 * Supprime du panel les composants du formulaire JPA.
	 */
	private void supprimerFormulaireJPA() {
		this.panel.remove(this.typePersistance);
		this.panel.remove(this.typeBdd);
		this.panel.remove(this.serveurLabel);
		this.panel.remove(this.serveur);
		this.panel.remove(this.portLabel);
		this.panel.remove(this.port);
		this.panel.remove(this.databaseLabel);
		this.panel.remove(this.database);
		this.panel.remove(this.userLabel);
		this.panel.remove(this.user);
		this.panel.remove(this.passLabel);
		this.panel.remove(this.pass);
	}

	/**
	 * Ajoute au panel les composants pour le formulaire JPA.
	 */
	private void ajouterFormulaireJPA() {
		this.panel.add(this.typePersistance, "split 2", 5);
		this.panel.add(this.typeBdd, "wrap", 6);
		this.panel.add(this.serveurLabel, 7);
		this.panel.add(this.serveur, "wrap", 8);
		this.panel.add(this.portLabel, 9);
		this.panel.add(this.port, "width 60!, wrap", 10);
		this.panel.add(this.databaseLabel, 11);
		this.panel.add(this.database, "wrap", 12);
		this.panel.add(this.userLabel, 13);
		this.panel.add(this.user, "wrap", 14);
		this.panel.add(this.passLabel, 15);
		this.panel.add(this.pass, "wrap", 16);
	}

	/**
	 * Affecte une nouvelle valeur au type de base de données.
	 * 
	 * @param typeDao
	 *            le nouveau typevaleur de base.
	 */
	public void setTypeDao(TypeFabriqueDao typeDao) {
		this.typeDao = typeDao;
	}

	/**
	 * Retourne le type de base de données en cours.
	 * 
	 * @return le type de base.
	 */
	public TypeFabriqueDao getTypeDao() {
		return this.typeDao;
	}

	/**
	 * Conserve les informations d'identification et test la connexion à la base
	 * de données puis continue les étapes d'initialisation jusqu'à lancer la
	 * fenêtre principale du programme.
	 */
	public void etapeSuivante() {
		// On enregistre les informations d'identification
		this.sauverProprietes();
		// On ajoute une barre de progression pour faire patienter l'utilisateur
		// pendant que la connexion à la base de donnée est testée si on a
		// rempli le formulaire JPA.
		if (Parametres.getParametres().getGestionnairePersistance()
				.equalsIgnoreCase("jpa")) {
			this.ajouterJProgressBar();
			this.setEnabled(false);
		} else {
			this.dispose();
		}

		if (this.threadAttente != null) {
			synchronized (this.threadAttente) {
				this.threadAttente.notify();
			}
		}
	}

	/**
	 * Ajoute une barre de progression faisant patienter l'utilisateur durant le
	 * test de la connexion à la base de données lors de l'utilisation du
	 * programme avec JPA.
	 */
	public void ajouterJProgressBar() {
		this.progressBar = new JProgressBar();
		this.progressBar.setIndeterminate(true);

		this.panel.remove(this.next);
		this.panel.remove(this.exit);

		this.panel.add(this.progressBar, "spanX 2, grow, wrap", 17);

		this.pack();
	}

	/**
	 * Supprime la barre de progression faisant patienter l'utilisateur durant
	 * le test de la connexion à la base de données lors de l'utilisation du
	 * programme avec JPA.
	 */
	public void supprimerJProgressBar() {
		this.panel.remove(this.progressBar);
		this.pack();
	}

	/**
	 * Permet d'enregistrer un thread qui doit être réveillé à la fermeture de
	 * l'interface.
	 * 
	 * @param obj
	 *            l'objet à réveiller.
	 */
	public void setThreadAttente(Object obj) {
		this.threadAttente = obj;
	}

	/**
	 * @param choisirFichier
	 *            le nouveau choisirFichier
	 */
	public void setChoisirFichier(JFileChooser choisirFichier) {
		this.choisirFichier = choisirFichier;
	}

	/**
	 * @return le choisirFichier
	 */
	public JFileChooser getChoisirFichier() {
		return this.choisirFichier;
	}

	/**
	 * Rempli le TextField db4oChemin avec le fichier selectionné par le
	 * JFileChooser
	 */
	public void afficheFileChooser() {
		int ret = this.choisirFichier.showOpenDialog(this);
		if (ret == JFileChooser.APPROVE_OPTION) {
			File file = this.choisirFichier.getSelectedFile();
			this.db4oChemin.setText(file.getAbsolutePath());
			this.choisirFichier.setCurrentDirectory(file.getParentFile());
		}
	}

	/**
	 * Accesseur pour connaître le type de formulaire sur lequel on se trouve.
	 * 
	 * @return le type de formulaire sur lequel on se trouve : JPA ou DB4O.
	 */
	public String getTypeFormulaire() {
		if (((String) this.typePersistance.getSelectedItem())
				.equalsIgnoreCase("JPA")) { return "JPA"; }
		return "DB4O";
	}

	/**
	 * Indique si tous les champs du formulaire JPA sont remplis.
	 * 
	 * @return <code>true</code> si tout les champs du formulaire JPA sont
	 *         remplis. <code>false</code> sinon.
	 */
	public boolean verifierFormulaireJPA() {
		if (this.serveur.getText().equals("") || this.port.getText().equals("")
				|| this.database.getText().equals("")
				|| this.user.getText().equals("")) { return false; }

		return true;
	}
}
